package com.core.filter;

import com.common.constants.ShiroConstant;
import com.common.model.entity.SysUser;
import com.common.utils.JwtUtils;
import com.common.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {

  private boolean allowOrigin = true;

  public JwtFilter() {}

  public JwtFilter(boolean allowOrigin) {
    this.allowOrigin = allowOrigin;
  }

  /**
   * 前置拦截处理（跨域支持，逻辑不变）
   */
  @Override
  protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
    HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
    if (allowOrigin) {
      log.info("JwtFilter ：preHandle()方法执行");
      log.info("Origin ：{}", httpServletRequest.getHeader("Origin"));
      httpServletResponse.setHeader("Access-control-Allow-Origin", "*");
      httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
      log.info("Access-Control-Allow-Headers ：{}", httpServletRequest.getHeader("Access-Control-Request-Headers"));
      httpServletResponse.setHeader("Access-Control-Allow-Headers", "*");
      httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
    }
    // 处理OPTIONS预检请求
    if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
      httpServletResponse.setStatus(HttpStatus.OK.value());
      return true;
    }
    return super.preHandle(request, response);
  }

  /**
   * 执行登录认证（逻辑不变，内部调用executeLogin）
   */
  @Override
  protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    try {
      return executeLogin(request, response);
    } catch (Exception e) {
      return false;
    }
  }

  /**
   * 核心：Token验证与用户信息获取（核心改造：用二级缓存替换RedisManager）
   */
  @Override
  protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
    HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
    HttpServletResponse httpResponse = WebUtils.toHttp(response);

    try {
      // 1. 从请求头获取Token（原有逻辑不变）
      String token = httpServletRequest.getHeader(JwtUtils.TOKEN_HEADER);
      log.info("JwtFilter ：从请求头 {} 中获取的token：{}", JwtUtils.TOKEN_HEADER, token);

      // 2. 处理Token为空的情况（原有逻辑不变）
      if (StringUtils.isBlank(token)) {
        httpResponse.setContentType("application/json;charset=UTF-8");
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        String msg = "{\"code\":401,\"msg\":\"token不能为空\"}";
        log.info("JwtFilter ：{}", msg);
        httpResponse.getWriter().write(msg);
        httpResponse.flushBuffer();
        return false;
      }

      // 3. 从Token解析用户名（原有逻辑不变）
      String userName = JwtUtils.getUserNameByToken(token);
      if (StringUtils.isBlank(userName)) {
        httpResponse.setContentType("application/json;charset=UTF-8");
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        String msg = "{\"code\":401,\"msg\":\"token验证失败，无法获取到用户名\"}";
        log.info("JwtFilter ：{}", msg);
        httpResponse.getWriter().write(msg);
        httpResponse.flushBuffer();
        return false;
      }

      // 4. 核心改造：从二级缓存获取用户信息（替换原RedisManager逻辑）
      // 4.1 通过Spring上下文获取二级缓存管理器
      CacheManager twoLevelCacheManager = SpringContextUtils.getBean("twoLevelCacheManager", CacheManager.class);
      // 4.2 获取用户缓存实例（使用无后缀缓存名称，与Realm/Service保持一致）
      Cache userCache = twoLevelCacheManager.getCache(ShiroConstant.CACHE_NAME_SYS_USER);
      // 4.3 缓存键：纯业务键（userName），与其他模块保持一致
      String userCacheKey = userName;

      // 4.4 从二级缓存查询用户
      Cache.ValueWrapper userCacheWrapper = userCache.get(userCacheKey);
      SysUser sysUser = null;
      if (userCacheWrapper != null) {
        sysUser = (SysUser) userCacheWrapper.get();
        log.info("JwtFilter ：从二级缓存获取用户信息：{}", sysUser);
      }

      // 5. 处理用户信息不存在的情况（原有逻辑不变）
      if (Objects.isNull(sysUser)) {
        httpResponse.setContentType("application/json;charset=UTF-8");
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        String msg = "{\"code\":401,\"msg\":\"token验证失败，无法获取到有效的用户信息\"}";
        log.info("JwtFilter ：{}", msg);
        httpResponse.getWriter().write(msg);
        httpResponse.flushBuffer();
        return false;
      }

      // 6. 验证Token有效性（原有逻辑不变）
      boolean isTokenValid = JwtUtils.verify(token, userName);
      if (!isTokenValid) {
        httpResponse.setContentType("application/json;charset=UTF-8");
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        String msg = "{\"code\":401,\"msg\":\"token验证失败，可能是 ---》 (token过期、签名错误、用户名不匹配) \"}";
        log.info("JwtFilter ：{}", msg);
        httpResponse.getWriter().write(msg);
        httpResponse.flushBuffer();
        return false;
      }

      // 7. Token验证通过，放行
      return true;
    } catch (Exception e) {
      // 统一异常处理（原有逻辑不变）
      httpResponse.setContentType("application/json;charset=UTF-8");
      httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      String msg = "{\"code\":500,\"msg\":\"" + e.getMessage() + "\"}";
      log.info("JwtFilter ：{}", msg);
      httpResponse.getWriter().write(msg);
      httpResponse.flushBuffer();
      return false;
    }
  }

  /**
   * Token登录成功后刷新Token（逻辑不变）
   */
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
    HttpServletResponse httpResponse = WebUtils.toHttp(response);
    // 请求token认证通过后刷新token
    String newToken = JwtUtils.refreshToken(token.getCredentials().toString(), JwtUtils.SECRET);
    if (newToken != null) {
      httpResponse.setHeader(JwtUtils.TOKEN_HEADER, newToken);
    }
    log.info("JwtFilter ：刷新token成功，新token为：{}", newToken);
    return true;
  }

  /**
   * Token登录失败处理（逻辑不变）
   */
  @Override
  protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    log.info("JwtFilter ：登录失败，异常信息为：{}", e.getMessage());
    return false;
  }
}
